﻿///
/// \file CComFunc.h
/// \brief 一些通用函数功能
///
/// \author 李荣业
/// \version V1.0
/// \date 2018.09.03
///

#ifndef CComFunc_H
#define CComFunc_H

#if defined(_WIN32)
#   include <windows.h>
#endif
#include "shared_export.h"
#include "comMacro.h"

uint64_t __eufy_get_timestamp();    // 获取高精度时间戳，执行速度很快(5ns), 单位100ns， 但实测Win10 64Bit频繁调用时更新精度约为1ms
namespace com
{
static constexpr uint64_t US_2_100NS = 10;
static constexpr uint64_t MS_2_100NS = (1000 * US_2_100NS);
static constexpr uint64_t S_2_100NS = (1000 * MS_2_100NS);

double now();   // 获取当前UTC时间，精度为ms, 速度极快, cost_time 15 ns

///
/// \brief qtGuiWait 等待sleep一段时间，同时Qt界面不会卡死，适合在轮询循环中调用
/// \param ms 等待的时间, 单位: 毫秒(ms)
///
void qtGuiWait(unsigned long ms, bool isResponseUser = false);

///
/// \brief delay Qt中调用等待一段时间，Qt界面不会卡死，同时响应用户输入事件
/// \param ms 等待的时间, 单位: 毫秒(ms)
///
void delay(int ms);
void busyWait(double ms = 0.1);  // 忙等待，不会响应用户输入事件

///
/// \brief split 字符串分割
/// \param src 源字符串，其中的分隔符会被置为'\0'
/// \param separator 分割字符串
/// \param dest 分割后的字符串数组
/// \param destLen dest数组允许长度
/// \return 分割后的字符串个数
///
int split(char * src, const char * separator, char ** dest, int destLen);

///
/// \brief clearDir Qt中调用，清空目录，保留目录本身
/// \param path 路径
/// \return 是否成功
///
bool clearDir(const QString & path);

///
/// \brief deleteDir 删除目录
/// \param path 路径
/// \return 是否成功
///
bool deleteDir(const QString & path);

///
/// \brief checkCreateDir 若目录不存在，创建目录
/// \param path 路径
/// \return 是否成功
///
bool checkCreateDir(const QString & path);

/// \brief 创建空目录，若目录已存在，清空它
bool createEmptyDir(const QString & path);

///
/// \brief average 计算平均值，会跳过值为0的数据
/// \param p 数据
/// \param len 数据个数
/// \return 平均值
///
double average(const double * p, int len);

inline double my_floor(double d) { const int64_t truncated = d; return d < 0 ? truncated - 1 : truncated; }
inline float my_floor(float d) { const int64_t truncated = d; return d < 0 ? truncated - 1 : truncated; }

/// \brief 对浮点数保留n位小数后输出
/// \param d 原有数据
/// \param n 保留的小数位数
/// \param isRounding 是否四舍五入
/// \return 转换后的数据
// double precision(double d, int n = 2, bool isRounding = true);
/// \brief 功能同上, 用模板优化后提高执行速度, 平均执行时间4.5ns, 测试平台: Win10 64位, Intel(R) Core(TM) i7-4800MQ CPU @ 2.70GHz 4核心
/// 优化之前平均执行时间为50ns，优化后速度提高了10倍
template<int n = 2, bool isRounding = true>
inline double precision(double d)
{
    constexpr static double times = CONST_POW(10, n);
    constexpr static double round = isRounding ? 0.5 : 0;
    // return std::floor(d * times + round) / times;
    return my_floor(d * times + round) / times;
}

template<int n = 2, bool isRounding = true>
inline float precision(float d)
{
    constexpr static float times = CONST_POW(10, n);
    constexpr static float round = isRounding ? 0.5 : 0;
    // return std::floor(d * times + round) / times;
    return my_floor(d * times + round) / times;
}

///
/// \brief loadTranslateFile Qt中调用，加载翻译文件
/// \param fileName 翻译文件名
/// \return 是否成功
///
bool loadTranslateFile(const QString & fileName);

/// \brief setProcessAutoRun 设置进程开机启动
/// \param appPath 需要设置/取消的自启动软件的“绝对路径”
/// \param enable 是否开机启动
void setProcessAutoRun(const QString & appPath, bool enable);
void setProcessAutoRun(bool enable);
bool checkAppIsRunning();
bool setProcessPriority(int prio);
bool setThreadPriority(int prio);
bool setThreadAffinity(int cpu);     // 设置线程亲和性, cpu从0开始
void SetExceptionFilter();  // 注冊异常捕获函数
bool checkNetConnect(const char * url); // 检测网络是否连接

template<typename T>
inline T my_abs(T v) { return v >= 0 ? v : -v; }

/// \brief 判断浮点数是否相等
inline bool isEqual(float a, float b) { return my_abs(a - b) <= 1e-6; }
inline bool isEqual(double a, double b) { return my_abs(a - b) <= 1e-6; }

/// \brief 判断浮点数是否为0
inline bool is0(double a) { return my_abs(a) <= 1e-6; }

inline bool areNumbersClose(int64_t a, int64_t b, int64_t c, float threshold)
{
    return my_abs(a - b) / (float)b < threshold && my_abs(b - c) / (float)c < threshold && my_abs(a - c) / (float)c < threshold;
}

const char * QString2_c_str(const QString & s, char * str, int size);

class Frequency
{
public:
    int update();   // 更新并返回频率
private:
    double t0_ = 0;
    int count_ = 0, fps_ = 0;
};

/// \brief 高精度计时器，可以计算精度为100ns级别的时间差
class ElapsedTimer
{
public:
    inline ElapsedTimer() { QueryPerformanceFrequency(&Frequency); ns100Freq = Frequency.QuadPart / 10000000.0; start(); }
    inline void start() { QueryPerformanceCounter(&t0_); }  // cost_time 14ns
    inline void restart() { start(); }
    inline uint64_t nsecsElapsed() const { return ns100Elapsed() * 100; }             // ns
    // 运行耗时14ns, 返回的单位是100ns
    inline uint64_t ns100Elapsed() const { LARGE_INTEGER t1; QueryPerformanceCounter(&t1); return (t1.QuadPart - t0_.QuadPart) / ns100Freq; }
    inline uint64_t usElapsed() const { return ns100Elapsed() / 10; }                 // us
    inline uint64_t elapsed() const { return ns100Elapsed() / 10000; }                // ms
    inline int64_t frequency() const { return Frequency.QuadPart; }

private:
    LARGE_INTEGER Frequency;
    double ns100Freq = 0.0;
    LARGE_INTEGER t0_;
};

class DurationRecorder
{
public:
    inline bool isIn() const { return 0 != beginTime_; }
    inline void begin(double created_at) { beginTime_ = created_at; ++times_; }
    inline void updateCurr(double now) { duration_ = hisDuration + now - beginTime_; }    // 需要频繁调用更新
    inline void finish(double now)
    {
        hisDuration += now - beginTime_;
        duration_ = hisDuration;
        beginTime_ = 0;
    }
    inline int duration() const { return duration_; }
    inline int times() const { return times_; }

private:
    double beginTime_ = 0;
    int times_ = 0, hisDuration = 0, duration_ = 0;            // 触发次数, 累计满足时长
};

} // namespace com

#endif /*CComFunc_H*/
